home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / distro.py < prev    next >
Encoding:
Python Source  |  2009-03-30  |  17.7 KB  |  462 lines

  1. #  distro.py - Provide a distro abstraction of the sources.list
  2. #
  3. #  Copyright (c) 2004-2007 Canonical Ltd.
  4. #                2006-2007 Sebastian Heinlein
  5. #
  6. #  Authors: Sebastian Heinlein <glatzor@ubuntu.com>
  7. #           Michael Vogt <mvo@debian.org>
  8. #
  9. #  This program is free software; you can redistribute it and/or
  10. #  modify it under the terms of the GNU General Public License as
  11. #  published by the Free Software Foundation; either version 2 of the
  12. #  License, or (at your option) any later version.
  13. #
  14. #  This program is distributed in the hope that it will be useful,
  15. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. #  GNU General Public License for more details.
  18. #
  19. #  You should have received a copy of the GNU General Public License
  20. #  along with this program; if not, write to the Free Software
  21. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  22. #  USA
  23.  
  24. import gettext
  25. import re
  26. import os
  27. import sys
  28.  
  29. from xml.etree.ElementTree import ElementTree
  30. import gettext
  31.  
  32.  
  33. def _(s):
  34.     return gettext.dgettext("python-apt", s)
  35.  
  36.  
  37. class NoDistroTemplateException(Exception):
  38.     pass
  39.  
  40.  
  41. class Distribution:
  42.  
  43.     def __init__(self, id, codename, description, release):
  44.         """ Container for distribution specific informations """
  45.         # LSB information
  46.         self.id = id
  47.         self.codename = codename
  48.         self.description = description
  49.         self.release = release
  50.  
  51.         self.binary_type = "deb"
  52.         self.source_type = "deb-src"
  53.  
  54.     def get_sources(self, sourceslist):
  55.         """
  56.         Find the corresponding template, main and child sources
  57.         for the distribution
  58.         """
  59.  
  60.         self.sourceslist = sourceslist
  61.         # corresponding sources
  62.         self.source_template = None
  63.         self.child_sources = []
  64.         self.main_sources = []
  65.         self.disabled_sources = []
  66.         self.cdrom_sources = []
  67.         self.download_comps = []
  68.         self.enabled_comps = []
  69.         self.cdrom_comps = []
  70.         self.used_media = []
  71.         self.get_source_code = False
  72.         self.source_code_sources = []
  73.  
  74.         # location of the sources
  75.         self.default_server = ""
  76.         self.main_server = ""
  77.         self.nearest_server = ""
  78.         self.used_servers = []
  79.  
  80.         # find the distro template
  81.         for template in self.sourceslist.matcher.templates:
  82.             if (self.is_codename(template.name) and
  83.                 template.distribution == self.id):
  84.                 #print "yeah! found a template for %s" % self.description
  85.                 #print template.description, template.base_uri, \
  86.                 #    template.components
  87.                 self.source_template = template
  88.                 break
  89.         if self.source_template is None:
  90.             raise NoDistroTemplateException("Error: could not find a "
  91.                                                 "distribution template")
  92.  
  93.         # find main and child sources
  94.         media = []
  95.         comps = []
  96.         cdrom_comps = []
  97.         enabled_comps = []
  98.         source_code = []
  99.         for source in self.sourceslist.list:
  100.             if (source.invalid == False and
  101.                 self.is_codename(source.dist) and
  102.                 source.template and
  103.                 self.is_codename(source.template.name)):
  104.                 #print "yeah! found a distro repo:  %s" % source.line
  105.                 # cdroms need do be handled differently
  106.                 if (source.uri.startswith("cdrom:") and 
  107.                     source.disabled == False):
  108.                     self.cdrom_sources.append(source)
  109.                     cdrom_comps.extend(source.comps)
  110.                 elif (source.uri.startswith("cdrom:") and 
  111.                       source.disabled == True):
  112.                     self.cdrom_sources.append(source)
  113.                 elif (source.type == self.binary_type and 
  114.                       source.disabled == False):
  115.                     self.main_sources.append(source)
  116.                     comps.extend(source.comps)
  117.                     media.append(source.uri)
  118.                 elif (source.type == self.binary_type and 
  119.                       source.disabled == True):
  120.                     self.disabled_sources.append(source)
  121.                 elif (source.type == self.source_type 
  122.                         and source.disabled == False):
  123.                     self.source_code_sources.append(source)
  124.                 elif (source.type == self.source_type and
  125.                       source.disabled == True):
  126.                     self.disabled_sources.append(source)
  127.             if (source.invalid == False and
  128.                 source.template in self.source_template.children):
  129.                 if (source.disabled == False 
  130.                     and source.type == self.binary_type):
  131.                     self.child_sources.append(source)
  132.                 elif (source.disabled == False 
  133.                       and source.type == self.source_type):
  134.                     self.source_code_sources.append(source)
  135.                 else:
  136.                     self.disabled_sources.append(source)
  137.         self.download_comps = set(comps)
  138.         self.cdrom_comps = set(cdrom_comps)
  139.         enabled_comps.extend(comps)
  140.         enabled_comps.extend(cdrom_comps)
  141.         self.enabled_comps = set(enabled_comps)
  142.         self.used_media = set(media)
  143.         self.get_mirrors()
  144.  
  145.     def get_mirrors(self, mirror_template=None):
  146.         """
  147.         Provide a set of mirrors where you can get the distribution from
  148.         """
  149.         # the main server is stored in the template
  150.         self.main_server = self.source_template.base_uri
  151.  
  152.         # other used servers
  153.         for medium in self.used_media:
  154.             if not medium.startswith("cdrom:"):
  155.                 # seems to be a network source
  156.                 self.used_servers.append(medium)
  157.  
  158.         if len(self.main_sources) == 0:
  159.             self.default_server = self.main_server
  160.         else:
  161.             self.default_server = self.main_sources[0].uri
  162.  
  163.         # get a list of country codes and real names
  164.         self.countries = {}
  165.         fname = "/usr/share/xml/iso-codes/iso_3166.xml"
  166.         if os.path.exists(fname):
  167.             et = ElementTree(file=fname)
  168.             it = et.getiterator('iso_3166_entry')
  169.             for elm in it:
  170.                 if elm.attrib.has_key("common_name"):
  171.                     descr = elm.attrib["common_name"]
  172.                 else:
  173.                     descr = elm.attrib["name"]
  174.                 if elm.attrib.has_key("alpha_2_code"):
  175.                     code = elm.attrib["alpha_2_code"]
  176.                 else:
  177.                     code = elm.attrib["alpha_3_code"]
  178.                 self.countries[code.lower()] = gettext.dgettext('iso_3166',descr)
  179.  
  180.         # try to guess the nearest mirror from the locale
  181.         self.country = None
  182.         self.country_code = None
  183.         locale = os.getenv("LANG", default="en_UK")
  184.         a = locale.find("_")
  185.         z = locale.find(".")
  186.         if z == -1:
  187.             z = len(locale)
  188.         country_code = locale[a+1:z].lower()
  189.  
  190.         if mirror_template:
  191.             self.nearest_server = mirror_template % country_code
  192.  
  193.         if country_code in self.countries:
  194.             self.country = self.countries[country_code]
  195.             self.country_code = country_code
  196.  
  197.     def _get_mirror_name(self, server):
  198.         ''' Try to get a human readable name for the main mirror of a country
  199.             Customize for different distributions '''
  200.         country = None
  201.         i = server.find("://")
  202.         l = server.find(".archive.ubuntu.com")
  203.         if i != -1 and l != -1:
  204.             country = server[i+len("://"):l]
  205.         if country in self.countries:
  206.             # TRANSLATORS: %s is a country
  207.             return _("Server for %s") % self.countries[country]
  208.         else:
  209.             return("%s" % server.rstrip("/ "))
  210.  
  211.     def get_server_list(self):
  212.         ''' Return a list of used and suggested servers '''
  213.  
  214.         def compare_mirrors(mir1, mir2):
  215.             ''' Helper function that handles comaprision of mirror urls
  216.                 that could contain trailing slashes'''
  217.             return re.match(mir1.strip("/ "), mir2.rstrip("/ "))
  218.  
  219.         # Store all available servers:
  220.         # Name, URI, active
  221.         mirrors = []
  222.         if (len(self.used_servers) < 1 or 
  223.             (len(self.used_servers) == 1 and 
  224.              compare_mirrors(self.used_servers[0], self.main_server))):
  225.             mirrors.append([_("Main server"), self.main_server, True])
  226.             if self.nearest_server:
  227.                 mirrors.append([self._get_mirror_name(self.nearest_server),
  228.                                 self.nearest_server, False])
  229.         elif (len(self.used_servers) == 1 and not 
  230.               compare_mirrors(self.used_servers[0], self.main_server)):
  231.             mirrors.append([_("Main server"), self.main_server, False])
  232.             # Only one server is used
  233.             server = self.used_servers[0]
  234.  
  235.             # Append the nearest server if it's not already used
  236.             if self.nearest_server:
  237.                 if not compare_mirrors(server, self.nearest_server):
  238.                     mirrors.append([self._get_mirror_name(self.nearest_server),
  239.                                     self.nearest_server, False])
  240.             if server:
  241.                 mirrors.append([self._get_mirror_name(server), server, True])
  242.  
  243.         elif len(self.used_servers) > 1:
  244.             # More than one server is used. Since we don't handle this case
  245.             # in the user interface we set "custom servers" to true and
  246.             # append a list of all used servers
  247.             mirrors.append([_("Main server"), self.main_server, False])
  248.             if self.nearest_server:
  249.                 mirrors.append([self._get_mirror_name(self.nearest_server),
  250.                                 self.nearest_server, False])
  251.             mirrors.append([_("Custom servers"), None, True])
  252.             for server in self.used_servers:
  253.                 mirror_entry = [self._get_mirror_name(server), server, False]
  254.                 if (compare_mirrors(server, self.nearest_server) or
  255.                     compare_mirrors(server, self.main_server)):
  256.                     continue
  257.                 elif not mirror_entry in mirrors:
  258.                     mirrors.append(mirror_entry)
  259.  
  260.         return mirrors
  261.  
  262.     def add_source(self, type=None,
  263.                  uri=None, dist=None, comps=None, comment=""):
  264.         """
  265.         Add distribution specific sources
  266.         """
  267.         if uri is None:
  268.             # FIXME: Add support for the server selector
  269.             uri = self.default_server
  270.         if dist is None:
  271.             dist = self.codename
  272.         if comps is None:
  273.             comps = list(self.enabled_comps)
  274.         if type is None:
  275.             type = self.binary_type
  276.         new_source = self.sourceslist.add(type, uri, dist, comps, comment)
  277.         # if source code is enabled add a deb-src line after the new
  278.         # source
  279.         if self.get_source_code == True and type == self.binary_type:
  280.             self.sourceslist.add(self.source_type, uri, dist, comps, comment,
  281.                                  file=new_source.file,
  282.                                  pos=self.sourceslist.list.index(new_source)+1)
  283.  
  284.     def enable_component(self, comp):
  285.         """
  286.         Enable a component in all main, child and source code sources
  287.         (excluding cdrom based sources)
  288.  
  289.         comp:         the component that should be enabled
  290.         """
  291.  
  292.         def add_component_only_once(source, comps_per_dist):
  293.             """
  294.             Check if we already added the component to the repository, since
  295.             a repository could be splitted into different apt lines. If not
  296.             add the component
  297.             """
  298.             # if we don't that distro, just reutnr (can happen for e.g.
  299.             # dapper-update only in deb-src
  300.             if source.dist not in comps_per_dist:
  301.                 return
  302.             # if we have seen this component already for this distro,
  303.             # return (nothing to do
  304.             if comp in comps_per_dist[source.dist]:
  305.                 return
  306.             # add it
  307.             source.comps.append(comp)
  308.             comps_per_dist[source.dist].add(comp)
  309.  
  310.         sources = []
  311.         sources.extend(self.main_sources)
  312.         sources.extend(self.child_sources)
  313.         # store what comps are enabled already per distro (where distro is
  314.         # e.g. "dapper", "dapper-updates")
  315.         comps_per_dist = {}
  316.         comps_per_sdist = {}
  317.         for s in sources:
  318.             if s.type == self.binary_type:
  319.                 if s.dist not in comps_per_dist:
  320.                     comps_per_dist[s.dist] = set()
  321.                     map(comps_per_dist[s.dist].add, s.comps)
  322.         for s in self.source_code_sources:
  323.             if s.type == self.source_type:
  324.                 if s.dist not in comps_per_sdist:
  325.                     comps_per_sdist[s.dist] = set()
  326.                     map(comps_per_sdist[s.dist].add, s.comps)
  327.  
  328.         # check if there is a main source at all
  329.         if len(self.main_sources) < 1:
  330.             # create a new main source
  331.             self.add_source(comps=["%s"%comp])
  332.         else:
  333.             # add the comp to all main, child and source code sources
  334.             for source in sources:
  335.                 add_component_only_once(source, comps_per_dist)
  336.  
  337.         # check if there is a main source code source at all
  338.         if self.get_source_code == True:
  339.             if len(self.source_code_sources) < 1:
  340.                 # create a new main source
  341.                 self.add_source(type=self.source_type, comps=["%s"%comp])
  342.             else:
  343.                 # add the comp to all main, child and source code sources
  344.                 for source in self.source_code_sources:
  345.                     add_component_only_once(source, comps_per_sdist)
  346.  
  347.     def disable_component(self, comp):
  348.         """
  349.         Disable a component in all main, child and source code sources
  350.         (excluding cdrom based sources)
  351.         """
  352.         sources = []
  353.         sources.extend(self.main_sources)
  354.         sources.extend(self.child_sources)
  355.         sources.extend(self.source_code_sources)
  356.         if comp in self.cdrom_comps:
  357.             sources = []
  358.             sources.extend(self.main_sources)
  359.         for source in sources:
  360.             if comp in source.comps:
  361.                 source.comps.remove(comp)
  362.                 if len(source.comps) < 1:
  363.                     self.sourceslist.remove(source)
  364.  
  365.     def change_server(self, uri):
  366.         ''' Change the server of all distro specific sources to
  367.             a given host '''
  368.  
  369.         def change_server_of_source(source, uri, seen):
  370.             # Avoid creating duplicate entries
  371.             source.uri = uri
  372.             for comp in source.comps:
  373.                 if [source.uri, source.dist, comp] in seen:
  374.                     source.comps.remove(comp)
  375.                 else:
  376.                     seen.append([source.uri, source.dist, comp])
  377.             if len(source.comps) < 1:
  378.                 self.sourceslist.remove(source)
  379.  
  380.         seen_binary = []
  381.         seen_source = []
  382.         self.default_server = uri
  383.         for source in self.main_sources:
  384.             change_server_of_source(source, uri, seen_binary)
  385.         for source in self.child_sources:
  386.             # Do not change the forces server of a child source
  387.             if (source.template.base_uri is None or 
  388.                 source.template.base_uri != source.uri):
  389.                 change_server_of_source(source, uri, seen_binary)
  390.         for source in self.source_code_sources:
  391.             change_server_of_source(source, uri, seen_source)
  392.  
  393.     def is_codename(self, name):
  394.         ''' Compare a given name with the release codename. '''
  395.         if name == self.codename:
  396.             return True
  397.         else:
  398.             return False
  399.  
  400.  
  401. class DebianDistribution(Distribution):
  402.     ''' Class to support specific Debian features '''
  403.  
  404.     def is_codename(self, name):
  405.         ''' Compare a given name with the release codename and check if
  406.             if it can be used as a synonym for a development releases '''
  407.         if name == self.codename or self.release in ("testing", "unstable"):
  408.             return True
  409.         else:
  410.             return False
  411.  
  412.     def _get_mirror_name(self, server):
  413.         ''' Try to get a human readable name for the main mirror of a country
  414.             Debian specific '''
  415.         country = None
  416.         i = server.find("://ftp.")
  417.         l = server.find(".debian.org")
  418.         if i != -1 and l != -1:
  419.             country = server[i+len("://ftp."):l]
  420.         if country in self.countries:
  421.             # TRANSLATORS: %s is a country
  422.             return _("Server for %s") % gettext.dgettext(
  423.                 "iso_3166", self.countries[country].rstrip()).rstrip()
  424.         else:
  425.             return("%s" % server.rstrip("/ "))
  426.  
  427.     def get_mirrors(self):
  428.         Distribution.get_mirrors(
  429.             self, mirror_template="http://ftp.%s.debian.org/debian/")
  430.  
  431.  
  432. class UbuntuDistribution(Distribution):
  433.     ''' Class to support specific Ubuntu features '''
  434.  
  435.     def get_mirrors(self):
  436.         Distribution.get_mirrors(
  437.             self, mirror_template="http://%s.archive.ubuntu.com/ubuntu/")
  438.  
  439.  
  440. def get_distro(id=None, codename=None, description=None, release=None):
  441.     """
  442.     Check the currently used distribution and return the corresponding
  443.     distriubtion class that supports distro specific features.
  444.  
  445.     If no paramter are given the distro will be auto detected via
  446.     a call to lsb-release
  447.     """
  448.     # make testing easier
  449.     if not (id and codename and description and release):
  450.         lsb_info = []
  451.         for lsb_option in ["-i", "-c", "-d", "-r"]:
  452.             pipe = os.popen("lsb_release %s -s" % lsb_option)
  453.             lsb_info.append(pipe.read().strip())
  454.             del pipe
  455.         (id, codename, description, release) = lsb_info
  456.     if id == "Ubuntu":
  457.         return UbuntuDistribution(id, codename, description, release)
  458.     elif id == "Debian":
  459.         return DebianDistribution(id, codename, description, release)
  460.     else:
  461.         return Distribution(id, codename, description, release)
  462.